package com.gm.census.service.impl;

import java.util.List;
import java.util.logging.Logger;

import com.gm.census.model.pojo.Congregation;
import com.gm.census.service.BaseService;
import com.gm.census.service.PagedListResult;
import com.gm.census.service.UserInfoFacade;
import com.google.appengine.api.NamespaceManager;
import com.google.appengine.api.datastore.Cursor;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Query;

public abstract class BaseServiceImpl<E, K> implements BaseService<E, K> {

	private static final Logger log = Logger.getLogger(BaseServiceImpl.class
			.getName());
	
	private UserInfoFacade userInfo;
	
	protected static Objectify begin() {
		return ObjectifyService.begin();
	}

	protected static Objectify beginTx() {
		return ObjectifyService.beginTransaction();
	}

	interface Command<T> {
		public T execute();
	}

	protected <T> T inNs(NsCommand<T> cmd) {
		String cns = NamespaceManager.get();
		if (cns == null)
			cns = NamespaceManager.getGoogleAppsNamespace();
		String ns = cmd.getNamespace();
		try {
			NamespaceManager.set(ns);
			return cmd.execute();
		} finally {
			NamespaceManager.set(cns);
		}
	}

	protected <T> T inTx(Command<T> cmd) {
		Objectify ofy = beginTx();
		T ret;
		try {
			ret = cmd.execute();
			ofy.getTxn().commit();
		} finally {
			if (ofy.getTxn().isActive()) {
				ofy.getTxn().rollback();
			}
		}
		return ret;
	}

	protected abstract static class NsCommand<T> implements Command<T> {
		private String ns;
		private static final Logger log = Logger.getLogger(NsCommand.class
				.getName());

		public NsCommand(String ns) {
			log.info(ns);
			this.ns = ns;
		}

		public final String getNamespace() {
			return ns;
		}
	}

	protected abstract class DefaultNSCmd<T> extends NsCommand<T> {

		public DefaultNSCmd() {
			super(NamespaceManager.getGoogleAppsNamespace());
		}

	}

	public E find(String ns, final Key<E> id) {
		return inNs(new NsCommand<E>(ns) {
			@Override
			public E execute() {
				return begin().find(id);
			}
		});
	}

	public Key<E> put(final String ns, final E e) {
		return inNs(new NsCommand<Key<E>>(ns) {
			@Override
			public Key<E> execute() {
				Key<E> k = begin().put(e);
				return k;
			}
		});
	}

	public void delete(final String ns, final Key<E> e) {
		inNs(new NsCommand<Object>(ns) {
			@Override
			public Object execute() {
				begin().delete(e);
				return null;
			}
		});
	}

	public E get(final String ns, final Key<E> id) {
		return inNs(new NsCommand<E>(ns) {
			@Override
			public E execute() {
				return begin().get(id);
			}
		});
	}

	public List<E> list(final String ns, final Class<E> clazz) {
		return inNs(new NsCommand<List<E>>(ns) {

			@Override
			public List<E> execute() {
				return begin().query(clazz)
						.limit(BaseService.DEFAULT_QUERY_PAGE).list();
			}

		});
	}

	public PagedListResult<List<E>> list(final String ns, final Class<E> clazz,
			final String index, final Integer pageSize) {
		return inNs(new NsCommand<GAEPagedListResult<E>>(ns) {

			@Override
			public GAEPagedListResult<E> execute() {
				Query<E> q = begin().query(clazz);
				if (pageSize > 0)
					q.limit(pageSize);
				else
					q.limit(BaseService.DEFAULT_QUERY_PAGE);

				if (index != null && index.length() > 0) {
					Cursor cursor = Cursor.fromWebSafeString(index);
					q.startCursor(cursor);
				}

				return new GAEPagedListResult<E>(q.iterator());
			}

		});
	}

	public PagedListResult<List<E>> list(final String ns, final Class<E> clazz,
			final String index) {
		return list(ns, clazz, index, null);
	}

	public UserInfoFacade getUserInfo() {
		return userInfo;
	}

	public void setUserInfo(UserInfoFacade userInfo) {
		this.userInfo = userInfo;
	}

}
